--[[
	- Garry's Mod Stranded -
	
	- Credits -
	
	- jA_c0p.
	- Wokkel.
	- prop_dynamic.
	- Muobman.
	- Chewgum.
	- RaBBish.
	- Mr. President.
	- Darkspider.
	
	And the model makers.
]]

GMS = {};

include("shared.lua");

GM.Tribes = {};
GM.NumTribes = 1;

AddCSLuaFile("shared.lua");
AddCSLuaFile("cl_init.lua");
AddCSLuaFile("cl_scoreboard.lua");
AddCSLuaFile("menutabs.lua");
AddCSLuaFile("cl_panels.lua");
AddCSLuaFile("unlocks.lua");
AddCSLuaFile("combinations.lua");
AddCSLuaFile("processes.lua");

include("unlocks.lua");
include("combinations.lua");

local PlayerMeta = FindMetaTable("Player");
local EntityMeta = FindMetaTable("Entity");

--Convars
CreateConVar( "gms_FreeBuild", "0" )
CreateConVar( "gms_FreeBuildSA", "0" )
CreateConVar( "gms_AllTools", "0", FCVAR_ARCHIVE )
CreateConVar( "gms_AutoSave", "1", FCVAR_ARCHIVE )
CreateConVar( "gms_AutoSaveTime", "3", FCVAR_ARCHIVE )
CreateConVar( "gms_physgun", "1" )
CreateConVar( "gms_ReproduceTrees", "0" )
CreateConVar( "gms_MaxReproducedTrees", "50", FCVAR_ARCHIVE )
CreateConVar( "gms_AllowSWEPSpawn", "0" )
CreateConVar( "gms_AllowSENTSpawn", "0" )
CreateConVar( "gms_SpreadFire", "0" )
CreateConVar( "gms_FadeRocks", "0" )
CreateConVar( "gms_CostsScale", "0.50" )
CreateConVar( "gms_Alerts", "1" )
CreateConVar( "gms_Campfire", "1" )
CreateConVar( "gms_PlantLimit", "10" )

SetGlobalInt( "plantlimit", GetConVarNumber( "gms_PlantLimit" ) )

--Vars
GM.NextSaved = 0
GM.NextLoaded = 0
PlayerIsDropping = 0

function GM:InitPostEntity()
	local entities = ents.GetAll();
	
	for _, entity in pairs(entities) do
		if (IsValid(entity) and entity:GetClass() == "prop_dynamic") then
			entity.owner = "World";
		end;
	end;
end;

-- A function to send a custom message.
function PlayerMeta:SendMessage(text, duration, color)
	local duration = duration or 3;
	local vector = Vector(color.r, color.g, color.b);

	umsg.Start("gms_sendmessage", self);
		umsg.String(text);
		umsg.Short(duration);
		umsg.Vector(vector);
	umsg.End();
end;

-- A function to return if the player is a buddy.
function PlayerMeta:IsBuddy(player)
	if (!player or !player:IsValid()) then return false; end;
	
	return table.HasValue(player.buddies, self);
end;

function PlayerMeta:SendAchievement(text)
	umsg.Start( "gms_sendachievement", self )
		umsg.String(text)
	umsg.End()
	
	self:EmitSound("music/hl1_song11.mp3");
end

function GM.StopSoundDelayed( sound, time )
	timer.Simple( time, GAMEMODE.StopSoundDelayed2, sound )
end

function GM.StopSoundDelayed2( sound )
	sound:Stop()
end

-- Called when you press F1.
function GM:ShowHelp(player)
	umsg.Start("gms_ToggleSkillsMenu", player)
	umsg.End()
end

-- Called when you press F2.
function GM:ShowTeam(player)
	umsg.Start("gms_quickmenu", player);
	umsg.End();
end;

function PlayerMeta:OpenCombiMenu( str )
	umsg.Start( "gms_OpenCombiMenu", self )
	umsg.String( str )
	umsg.End()
end

function PlayerMeta:CloseCombiMenu()
	umsg.Start( "gms_CloseCombiMenu", self )
	umsg.End()
end

/*---------------------------------------------------------
  Skill functions
---------------------------------------------------------*/
function PlayerMeta:SetSkill( skill, int )
	if !self.Skills[ skill ] then self.Skills[ skill ] = 0 end

	self.Skills[ skill ] = int
	
	umsg.Start( "gms_SetSkill", self )
		umsg.String( skill )
		umsg.Short( self:GetSkill( skill ) )
	umsg.End()
end

function PlayerMeta:GetSkill(skill)
	self:SetNWInt(skill, self.Skills[skill]) 
	return self.Skills[skill] or 0	
end

function PlayerMeta:IncSkill(skill,int)
	if !self.Skills[skill] then self:SetSkill(skill,0) end
	if !self.Experience[skill] then self:SetXP(skill,0) end

	if skill != "Survival" then
		self:IncXP("Survival",20)
		self:SendMessage(skill.." +1",3,Color(10,200,10,255))
	else
		self.MaxResources = self.MaxResources + 5

		self:SendAchievement("Level Up!")
	end
         
	self.Skills[skill] = self.Skills[skill] + int
         
	umsg.Start("gms_SetSkill",self)
	umsg.String(skill)
	umsg.Short(self:GetSkill(skill))
	umsg.End()
         
	self:CheckForUnlocks()
end

function PlayerMeta:DecSkill(skill,int)
	self.Skills[skill] = self.Skills[skill] - int

	umsg.Start("gms_SetSkill",self)
	umsg.String(skill)
	umsg.Short(self:GetSkill(skill))
	umsg.End()
end

/*---------------------------------------------------------
  XP functions
---------------------------------------------------------*/
function PlayerMeta:SetXP(skill,int)
	if !self.Skills[skill] then self:SetSkill(skill,0) end
	if !self.Experience[skill] then self.Experience[skill] = 0 end

	self.Experience[skill] = int

	umsg.Start("gms_SetXP",self)
	umsg.String(skill)
	umsg.Short(self:GetXP(skill))
	umsg.End()
end

function PlayerMeta:GetXP(skill)
	return self.Experience[skill] or 0
end

function PlayerMeta:IncXP(skill,int)
	if !self.Skills[skill] then self.Skills[skill] = 0 end
	if !self.Experience[skill] then self.Experience[skill] = 0 end

	if self.Experience[skill] + int >= 100 then
		self.Experience[skill] = 0
		self:IncSkill(skill,1)
	else
		self.Experience[skill] = self.Experience[skill] + int
	end

	umsg.Start("gms_SetXP",self)
	umsg.String(skill)
	umsg.Short(self:GetXP(skill))
	umsg.End()
end

function PlayerMeta:DecXP(skill,int)
	self.Experience[skill] = self.Experience[skill] - int

	umsg.Start("gms_SetXP",self)
	umsg.String(skill)
	umsg.Short(self:GetXP(skill))
	umsg.End()
end

/*---------------------------------------------------------
  Resource functions
---------------------------------------------------------*/
function PlayerMeta:SetResource(resource,int)
	if !self.Resources[resource] then self.Resources[resource] = 0 end

	self.Resources[resource] = int

	umsg.Start("gms_SetResource",self)
	umsg.String(resource)
	umsg.Short(int)
	umsg.End()
end

function PlayerMeta:GetResource(resource)
	return self.Resources[resource] or 0
end

function PlayerMeta:IncResource(resource,int)
	if !self.Resources[resource] then self.Resources[resource] = 0 end
	local all = self:GetAllResources()
	local max = self.MaxResources

	if all + int > max then
		self.Resources[resource] = self.Resources[resource] + (max - all)
		self:DropResource(resource,(all + int) - max)
		self:SendMessage("You can't carry anymore!",3,Color(200,0,0,255))
	else
		self.Resources[resource] = self.Resources[resource] + int
	end

	umsg.Start("gms_SetResource",self)
	umsg.String(resource)
	umsg.Short(self:GetResource(resource))
	umsg.End()
end

function PlayerMeta:GetAllResources()
	local num = 0

	for k,v in pairs(self.Resources) do
		num = num + v
	end

	return num
end

-- Gmods spawn function already autocorrects. Would be a waste to not use it.
function PlayerMeta:CreateBuildingSite(position, angle, model, class, cost)
	local entity = ents.Create("gms_buildsite");
	entity:SetAngles(angle);
	entity:SetPos(position);
	entity:Setup(model, class);
	entity:Spawn();
	
	entity.Costs = cost;
	entity.owner = self;
	entity.Player = self;
	
	return entity;
end;

-- Spawning from a trace needs some correction.
function PlayerMeta:CreateStructureBuildingSite(pos, angle, model, class, cost, name)
	local rep = ents.Create("gms_buildsite")
	local tbl = rep:GetTable()
	local str = ":"
	for k,v in pairs(cost) do
		str = str.." "..k.." (x"..v..")"
	end
	rep.owner = self
	rep:SetAngles(angle)
	rep.Costs = cost
	tbl:Setup(model,class)
	rep:SetPos(pos)
	rep.Name = name
	rep:SetNetworkedString('Name', name)
	rep:SetNetworkedString('Resources', str)
	rep:Spawn()
	local cormin,cormax = rep:WorldSpaceAABB( )
	local offset = cormax-cormin 
	rep:SetPos(Vector(pos.x,pos.y,pos.z + (offset.z/2)))
	if model != "models/props_c17/factorymachine01.mdl" and model != "models/props_c17/furniturefireplace001a.mdl" and model != "models/Gibs/airboat_broken_engine.mdl" and model != "models/props_c17/furniturestove001a.mdl" and model != "models/props_wasteland/controlroom_desk001b.mdl" then
		rep:DropToGround()	
	end
	self:SetNetworkedEntity('Hasbuildingsite', rep)
	rep.Player = self
	return rep
end

function PlayerMeta:GetBuildingSite()
	return self:GetNetworkedEntity("Hasbuildingsite")
end

function PlayerMeta:DecResource(resource,int)
	if !self.Resources[resource] then self.Resources[resource] = 0 end
	self.Resources[resource] = self.Resources[resource] - int

	umsg.Start("gms_SetResource",self)
	umsg.String(resource)
	umsg.Short(self:GetResource(resource))
	umsg.End()
end

-- A function to drop a resource.
function PlayerMeta:DropResource(resource, amount)
	local nearby = {};

	for _, box in pairs(ents.FindByClass("gms_resourcedrop")) do
		if (box:GetPos():Distance(self:GetPos()) < 150 and box.Type == resource) then
			table.insert(nearby, box);
		end;
	end;

	if (#nearby > 0) then
		local entity = nearby[1];
		entity.Amount = entity.Amount +amount;
		
		entity:ApplyDropInfoInstant();
	else
		local entity = ents.Create("gms_resourcedrop");
		entity:SetPos(self:EyeTrace(60).HitPos +Vector(0, 0, 15));
		entity:SetAngles(self:GetAngles());
		entity:Spawn();

		entity:GetPhysicsObject():Wake();

		entity.Type = resource;
		entity.Amount = amount;
		entity.owner = self;

		entity:ApplyDropInfo();
	end;
end;

-- A function to set resource information (delayed).
function EntityMeta:ApplyDropInfo()
	timer.Simple(0.3, self.ApplyDropInfoInstant, self);
end;

-- A function to set resource information.
function EntityMeta:ApplyDropInfoInstant()
	for _, player in pairs(player.GetAll()) do
		self.Type = self.Type or "Error";

		umsg.Start("gms_SetResourceDropInfo", player);
			umsg.Long(self:EntIndex());
			umsg.String(string.gsub(self.Type, "_", " "));
			umsg.Short(self.Amount);
		umsg.End();
	end;
end;

-- A function to set a players hunger.
function PlayerMeta:SetFood(int)
	if int > 1000 then
		int = 1000
	end

	self.Hunger = int
	self:UpdateNeeds()
end

-- A function to get a players hunger.
function PlayerMeta:GetFood()
	return self.Hunger
end

/*---------------------------------------------------------
  Water functions
---------------------------------------------------------*/
function PlayerMeta:SetThirst(int)
	if int > 1000 then
		int = 1000
	end

	self.Thirst = int
	self:UpdateNeeds()
end

function PlayerMeta:GetThirst()
	return self.Thirst
end

/*---------------------------------------------------------
  Sleep functions
---------------------------------------------------------*/
function PlayerMeta:SetSleepiness(int)
	if int > 1000 then
		int = 1000
	end

	self.Sleepiness = int
	self:UpdateNeeds()
end

function PlayerMeta:GetSleepiness()
	return self.Sleepiness
end

/*---------------------------------------------------------
  Custom health functions
---------------------------------------------------------*/
function PlayerMeta:Heal(int)
	local max = 100

	if self:HasUnlock("Adept_Survivalist") then max = 150 end
	if self:HasUnlock("Master_Survivalist") then max = 200 end

	if self:Health() + int > max then
		self:SetHealth(max)
	else
		self:SetHealth(self:Health() + int)
	end
end

/*---------------------------------------------------------
  Unlock functions
---------------------------------------------------------*/
function PlayerMeta:AddUnlock(text)

	self.FeatureUnlocks[text] = 1

	umsg.Start("gms_AddUnlock",self)
	umsg.String(text)
	umsg.End()

	if GMS.FeatureUnlocks[text].OnUnlock then GMS.FeatureUnlocks[text].OnUnlock(self) end
end

function PlayerMeta:HasUnlock(text)
	if self.FeatureUnlocks[text] then return true end
	return false
end

function PlayerMeta:CheckForUnlocks()
	for k,unlock in pairs(GMS.FeatureUnlocks) do
		if !self:HasUnlock(k) then
			local NrReqs = 0

			for skill,value in pairs(unlock.Req) do
				if self:GetSkill(skill) >= value then
					NrReqs = NrReqs + 1
				end
			end

			if NrReqs == table.Count(unlock.Req) then
				self:AddUnlock(k)
			end
		end
	end
end

/*---------------------------------------------------------
  Model check functions
---------------------------------------------------------*/
function EntityMeta:IsTreeModel()
	local trees = table.Add(GMS.TreeModels,GMS.AdditionalTreeModels)
	for k,v in pairs(trees) do
		if string.lower(v) == self:GetModel() or string.gsub(string.lower(v),"/","\\") == self:GetModel() then
			return true
		end
	end

	return false
end

function EntityMeta:IsBerryBushModel()
	local mdl = "models/props/pi_shrub.mdl"
	if mdl == self:GetModel() or string.gsub(string.lower(mdl),"/","\\") == self:GetModel() then
		return true
	end

	return false
end

function EntityMeta:IsGrainModel()
	local mdl = "models/props_foliage/cattails.mdl"
	if mdl == self:GetModel() or string.gsub(string.lower(mdl),"/","\\") == self:GetModel() then
		return true
	end

	return false
end

function EntityMeta:IsFoodModel()
	for k,v in pairs(GMS.EdibleModels) do
		if string.lower(v) == self:GetModel() or string.gsub(string.lower(v),"/","\\") == self:GetModel() then
			return true
		end
	end

	return false
end

function EntityMeta:IsRockModel()
	local rocks = table.Add(GMS.RockModels,GMS.AdditionalRockModels)
	for k,v in pairs(rocks) do
		if string.lower(v) == self:GetModel() or string.gsub(string.lower(v),"/","\\") == self:GetModel() then
			return true
		end
	end

	return false
end

function EntityMeta:IsProp()
	local cls = self:GetClass()

	if (cls == "prop_physics" or cls == "prop_physics_multiplayer" or cls == "prop_dynamic") then
		return true
	end

	return false
end

function EntityMeta:IsSleepingFurniture()
	for _,v in ipairs(GMS.SleepingFurniture) do
		if string.lower(v) == self:GetModel() or string.gsub(string.lower(v),"/","\\") == self:GetModel() then
			return true
		end
	end

	return false
end

/*---------------------------------------------------------
  Other utilities
---------------------------------------------------------*/
function string.Capitalize(str)
	local str = string.Explode("_",str)
	for k,v in pairs(str) do
		str[k] = string.upper(string.sub(v,1,1))..string.sub(v,2)
	end

	str = string.Implode("_",str)
	return str
end

function PlayerMeta:EyeTrace(dist)
	local trace = {}
	trace.start = self:GetShootPos()
	trace.endpos = trace.start + (self:GetAimVector() * dist)
	trace.filter = self

	return util.TraceLine(trace)
end

function EntityMeta:DropToGround()
	local trace = {}
	trace.start = self:GetPos()
	trace.endpos = trace.start + Vector(0,0,-100000)
	trace.mask = MASK_SOLID_BRUSHONLY
	trace.filter = self

	local tr = util.TraceLine(trace)

	self:SetPos(tr.HitPos)
end

function GMS.ClassIsNearby(pos,class,range)
	local nearby = false
	for k,v in pairs(ents.FindInSphere(pos,range)) do
		if v:GetClass() == class and (pos-Vector(v:LocalToWorld(v:OBBCenter()).x,v:LocalToWorld(v:OBBCenter()).y,pos.z)):Length() <= range then
			nearby = true
		end
	end

	return nearby
end

function GMS.IsInWater(pos)
	local trace = {}
	trace.start = pos
	trace.endpos = pos + Vector(0,0,1)
	trace.mask = MASK_WATER | MASK_SOLID

	local tr = util.TraceLine(trace)

	return tr.Hit
end
/*---------------------------------------------------------

  Automatic tree reproduction

---------------------------------------------------------*/
function GM.ReproduceTrees()
	local GM = GAMEMODE
	if GetConVarNumber("gms_ReproduceTrees") == 1 then
		local trees = {}
		for k,v in pairs(ents.GetAll()) do
			if v:IsTreeModel() then
				table.insert(trees,v)
			end
		end

		if #trees < GetConVarNumber("gms_MaxReproducedTrees") then
			for k,ent in pairs(trees) do
				local num = math.random(1,3)

				if num == 1 then
					local nearby = {}
					for k,v in pairs(ents.FindInSphere(ent:GetPos(),50)) do
						if v:GetClass() == "gms_seed" or v:IsProp() then
							table.insert(nearby,v)
						end
					end

					if #nearby < 3 then
						local pos = ent:GetPos() + Vector(math.random(-500,500),math.random(-500,500),0)
						local retries = 50

						while (pos:Distance(ent:GetPos()) < 200 or GMS.ClassIsNearby(pos,"prop_physics",100)) and retries > 0 do
							pos = ent:GetPos() + Vector(math.random(-300,300),math.random(-300,300),0)
							retries = retries - 1
						end

						local pos = pos + Vector(0,0,500)

						local seed = ents.Create("gms_seed")
						seed:SetPos(pos)
						seed:DropToGround()
						seed:Setup("tree",180)
						seed:SetNetworkedString("Owner", "World")
						seed:Spawn()		
					end
				end
			end
		end
		if #trees == 0 then
			local info = {}
			for i = 1,20 do
				info.pos = Vector(math.random(-10000,10000),math.random(-10000,10000),1000)
				info.Retries = 50

				--Find pos in world
				while util.IsInWorld(info.pos) == false and info.Retries > 0 do
					info.pos = Vector(math.random(-10000,10000),math.random(-10000,10000),1000)
					info.Retries = info.Retries - 1
				end

				--Find ground
				local trace = {}
				trace.start = info.pos
				trace.endpos = trace.start + Vector(0,0,-100000)
				trace.mask = MASK_SOLID_BRUSHONLY

				local groundtrace = util.TraceLine(trace)

				--Assure space
				local nearby = ents.FindInSphere(groundtrace.HitPos,200)
				info.HasSpace = true

				for k,v in pairs(nearby) do
					if v:IsProp() then
						info.HasSpace = false
					end
				end

				--Find sky
				local trace = {}
				trace.start = groundtrace.HitPos
				trace.endpos = trace.start + Vector(0,0,100000)

				local skytrace = util.TraceLine(trace)

				--Find water?
				local trace = {}
				trace.start = groundtrace.HitPos
				trace.endpos = trace.start + Vector(0,0,1)
				trace.mask = MASK_WATER

				local watertrace = util.TraceLine(trace)

				--All a go, make entity
				if info.HasSpace and skytrace.HitSky and !watertrace.Hit and (groundtrace.MatType == MAT_DIRT or groundtrace.MatType == MAT_GRASS or groundtrace.MatType == MAT_SAND) then
					local seed = ents.Create("gms_seed")
					seed:SetPos(groundtrace.HitPos)
					seed:DropToGround()
					seed:Setup("tree",180 + math.random(-20,20))
					seed:SetNetworkedString("Owner", "World")
					seed:Spawn()
				end
			end
		end
	end

	timer.Simple(math.random(1,3) * 60,GM.ReproduceTrees)
end

timer.Simple(60,GM.ReproduceTrees)

/*---------------------------------------------------------

  Admin terraforming

---------------------------------------------------------*/
function GM.CreateRandomTree(player)
	if !player:IsAdmin() then 
		player:SendMessage("You need admin rights for this!",3,Color(200,0,0,255))
	return end

	local tr = player:EyeTrace(10000)

	GAMEMODE.MakeTree(tr.HitPos)
end

concommand.Add("gms_admin_maketree",GM.CreateRandomTree)

function GM.CreateRandomRock(player)
	if !player:IsAdmin() then 
		player:SendMessage("You need admin rights for this!",3,Color(200,0,0,255))
	return end
	local tr = player:EyeTrace(10000)
	local ent = ents.Create("prop_physics")
	ent:SetAngles(Angle(0,math.random(1,360),0))
	ent:SetModel(GMS.RockModels[math.random(1,#GMS.RockModels)])
	ent:SetPos(tr.HitPos)
	ent:Spawn()
	ent:SetNetworkedString("Owner", "World")
	ent:Fadein()
	ent:GetPhysicsObject():EnableMotion(false)
	ent.StrandedProtected = true
	
end

concommand.Add("gms_admin_makerock",GM.CreateRandomRock)

function GM.CreateRandomFood(player)
	if !player:IsAdmin() then 
		player:SendMessage("You need admin rights for this!",3,Color(200,0,0,255))
	return end

	local tr = player:EyeTrace(10000)

	local ent = ents.Create("prop_physics")
	ent:SetAngles(Angle(0,math.random(1,360),0))
	ent:SetModel(GMS.EdibleModels[math.random(1,#GMS.EdibleModels)])
	ent:SetPos(tr.HitPos + Vector(0,0,10))		 
	ent:Spawn()
end

concommand.Add("gms_admin_makefood",GM.CreateRandomFood)

function GM.MakeAntlionBarrow(player,cmd,args)
	if !player:IsAdmin() then
		player:SendMessage("You need admin rights for this!",3,Color(200,0,0,255))
	return end

	if !args[1] then
		player:SendMessage("Specify max antlions!",3,Color(200,0,0,255))
	return end

	local tr = player:EyeTrace(10000)

	local ent = ents.Create("gms_antlionbarrow")
	ent:SetPos(tr.HitPos)
	ent:Spawn()
	ent:SetNetworkedString("Owner", "World")
	ent:SetKeyValue("MaxAntlions",args[1])
	Msg("Sending keyvalue: "..args[1].."\n")
end

concommand.Add("gms_admin_MakeAntlionBarrow",GM.MakeAntlionBarrow)

function GM.CreateRandomBush(player)
	local GM = GAMEMODE
	if !player:IsAdmin() then 
		player:SendMessage("You need admin rights for this!",3,Color(200,0,0,255))
	return end

	local tr = player:EyeTrace(10000)
	local typ = math.random(1,5)
	local pos = tr.HitPos

	if typ == 1 then
		GM.MakeMelon(pos,math.random(1,2), player)
	elseif typ == 2 then
		GM.MakeBanana(pos,math.random(1,2), player)
	elseif typ == 3 then
		GM.MakeOrange(pos,math.random(1,2), player)
	elseif typ == 4 then
		GM.MakeBush(pos, player)
	elseif typ == 5 then
		GM.MakeGrain(pos, player)
	end
end

concommand.Add("gms_admin_makebush",GM.CreateRandomBush)

function GM.PopulateArea(g_Player, cmd, args)
	local GM = GAMEMODE
	if !g_Player:IsAdmin() then
		g_Player:SendMessage("You need admin rights for this!",3,Color(200,0,0,255))
	return end

	if !args[1] or !args[2] or !args[3] then
		g_Player:SendMessage("You need to specify <type> <amount> <radius>",3,Color(200,0,0,255))
	return end

	for k,v in pairs(player.GetAll()) do
		v:SendMessage("Populating area...",3,Color(255,255,255,255))
	end

	--Population time
	local Amount = tonumber(args[2])
	local info = {}
	info.Amount = Amount

	if Amount > 200 then 
		g_Player:SendMessage("Auto-capped at 200 props.",3,Color(200,0,0,255))
		info.Amount = 200
	end

	local Type = args[1]
	local Amount = info.Amount
	local Radius = tonumber(args[3])

	--Find playertrace
	local playertrace = g_Player:EyeTrace(10000)

	for i = 1,Amount do
		info.pos = playertrace.HitPos + Vector(math.random(-Radius,Radius),math.random(-Radius,Radius),1000)
		info.Retries = 50

		--Find pos in world
		while util.IsInWorld(info.pos) == false and info.Retries > 0 do
			info.pos = playertrace.HitPos + Vector(math.random(-Radius,Radius),math.random(-Radius,Radius),1000)
			info.Retries = info.Retries - 1
		end

		--Find ground
		local trace = {}
		trace.start = info.pos
		trace.endpos = trace.start + Vector(0,0,-100000)
		trace.mask = MASK_SOLID_BRUSHONLY

		local groundtrace = util.TraceLine(trace)

		--Assure space
		local nearby = ents.FindInSphere(groundtrace.HitPos,200)
		info.HasSpace = true

		for k,v in pairs(nearby) do
			if v:IsProp() then
				info.HasSpace = false
			end
		end

		--Find sky
		local trace = {}
		trace.start = groundtrace.HitPos
		trace.endpos = trace.start + Vector(0,0,100000)

		local skytrace = util.TraceLine(trace)

		--Find water?
		local trace = {}
		trace.start = groundtrace.HitPos
		trace.endpos = trace.start + Vector(0,0,1)
		trace.mask = MASK_WATER

		local watertrace = util.TraceLine(trace)

		--All a go, make entity
		if Type == "Trees" then
			if info.HasSpace and skytrace.HitSky and !watertrace.Hit and (groundtrace.MatType == MAT_DIRT or groundtrace.MatType == MAT_GRASS or groundtrace.MatType == MAT_SAND) then
				GAMEMODE.MakeTree(groundtrace.HitPos)
			end
		elseif Type == "Rocks" then
			if !watertrace.Hit and (groundtrace.MatType == MAT_DIRT or groundtrace.MatType == MAT_GRASS or groundtrace.MatType == MAT_SAND) then
				local ent = ents.Create("prop_physics")
				ent:SetAngles(Angle(0,math.random(1,360),0))
				ent:SetModel(GMS.RockModels[math.random(1,#GMS.RockModels)])
				ent:SetPos(groundtrace.HitPos)
				ent:Spawn()
				ent:SetNetworkedString("Owner", "World")
				ent:Fadein()
				ent.StrandedProtected = true
				ent:GetPhysicsObject():EnableMotion(false)
				
			end
		elseif Type == "Random_Plant" and info.HasSpace then
			if !watertrace.Hit and (groundtrace.MatType == MAT_DIRT or groundtrace.MatType == MAT_GRASS or groundtrace.MatType == MAT_SAND) then
				local typ = math.random(1,5)
				local pos = groundtrace.HitPos

				if typ == 1 then
					GM.MakeMelon(pos,math.random(1,2), g_Player)
				elseif typ == 2 then
					GM.MakeBanana(pos,math.random(1,2), g_Player)
				elseif typ == 3 then
					GM.MakeOrange(pos,math.random(1,2), g_Player)
				elseif typ == 4 then
					GM.MakeBush(pos, g_Player)
				elseif typ == 5 then
					GM.MakeGrain(pos, g_Player)
				end
			end
		end
	end

	--Finished
	for k,v in pairs(player.GetAll()) do
		v:SendMessage("Done!",3,Color(255,255,255,255))
	end
end

concommand.Add("gms_admin_PopulateArea",GM.PopulateArea)

function GM.TreeRockCleanup(player)
	if !player:IsAdmin() then
		player:SendMessage("You need admin rights for this!",3,Color(200,0,0,255))
	return end

	for k,v in pairs(ents.GetAll()) do
		if v:IsRockModel() or v:IsTreeModel() then
			
			for k,tbl in pairs(GAMEMODE.RisingProps) do
				if tbl.Entity == v then
					table.remove(GAMEMODE.RisingProps,k)
				end
			end
			
			for k,tbl in pairs(GAMEMODE.SinkingProps) do
				if tbl.Entity == v then
					table.remove(GAMEMODE.SinkingProps,k)
				end
			end
			
			for k,ent in pairs(GAMEMODE.FadingInProps) do
				if ent == v then
					table.remove(GAMEMODE.FadingInProps,k)
				end
			end
			
			v:Fadeout()
			
		end
	end
	
	for k,v in pairs(player.GetAll()) do v:SendMessage("Cleared map.",3,Color(255,255,255,255)) end
	
end

concommand.Add("gms_admin_clearmap",GM.TreeRockCleanup)
/*---------------------------------------------------------

  Prop fadeout

---------------------------------------------------------*/
GMS.FadingOutProps = {}
GMS.FadingInProps = {}

function EntityMeta:Fadeout(speed)
	local speed = speed or 1

	for k,v in pairs(player.GetAll()) do
		umsg.Start("gms_CreateFadingProp",v)
		umsg.String(self:GetModel())
		umsg.Vector(self:GetPos())
		umsg.Vector(self:GetAngles():Forward())
		umsg.Short(math.Round(speed))
		umsg.End()
	end

	self:Remove()
end

--Fadein is serverside
function EntityMeta:Fadein()
	self.AlphaFade = 0
	self:SetColor(255,255,255,0)
	table.insert(GMS.FadingInProps,self)
end

/*---------------------------------------------------------

  Prop rising/lowering

---------------------------------------------------------*/
GM.RisingProps = {}
GM.SinkingProps = {}

function EntityMeta:RiseFromGround(speed,altmax)
	local speed = speed or 1
	local max;

	if !altmax then
		min,max = self:WorldSpaceAABB()
		max = max.z
	else
		max = altmax
	end

	local tbl = {}
	tbl.Origin = self:GetPos().z
	tbl.Speed = speed
	tbl.Entity = self

	self:SetPos(self:GetPos() + Vector(0,0,-max + 10))
	table.insert(GAMEMODE.RisingProps,tbl)
end

function EntityMeta:SinkIntoGround(speed)
	local speed = speed or 1

	local tbl = {}
	tbl.Origin = self:GetPos().z
	tbl.Speed = speed
	tbl.Entity = self
	tbl.Height = max

	table.insert(GAMEMODE.SinkingProps,tbl)
end

-- A command to load the player.
concommand.Add("gms_plready", function(player)
	if (player.Ready) then return; end;
	
	local unique = player:UniqueID();
	
	player.Skills 			= {};
	player.buddies 			= {};
	player.Resources 		= {};
	player.Experience 		= {};
	player.FeatureUnlocks 	= {};
	
	player.drinkDelay = 0;
	player.CanSpawnProp = true;
	
	if (file.Exists("GMStranded/Saves/" .. unique .. ".txt")) then
		local data = util.KeyValuesToTable(file.Read("GMStranded/Saves/" .. unique .. ".txt"));

		if (data.skills) then
			for k, v in pairs(data.skills) do
				player:SetSkill(string.Capitalize(k), v);
			end;
		end;

		if (data.experience) then
			for k, v in pairs(data.experience) do
				player:SetXP(string.Capitalize(k), v);
			end;
		end;

		if (data.unlocks) then 
			for k, v in pairs(data.unlocks) do
				player.FeatureUnlocks[string.Capitalize(k)] = v;
			end;
		end;

		for k, v in pairs(GMS.FeatureUnlocks) do
			if (player:HasUnlock(k) and v.OnUnlock) then
				v.OnUnlock(player);
			end;
		end;

		if (!player.Skills.Survival) then
			player.Skills.Survival = 0;
		end;

		player.MaxResources = (player.Skills.Survival *5) +25;

		player:SendMessage("Loaded data successfully.", 10, Color(255, 255, 255));
		player:SendMessage("Last visited on " .. data.date .. ", enjoy your stay.", 10, Color(255, 255, 255));
	else
		player.MaxResources = 25;
		player:SetXP("Survival", 0);
		player:SetSkill("Survival", 0);
	end;

	for _, entity in pairs(ents.GetAll()) do
		if (IsValid(entity)) then
			if (entity.owner and entity.owner == player) then
				--[[
					------------ ADD PLANT LIMIT HERE ----------
				]]
			end;
			
			if (entity:GetClass() == "gms_resourcedrop") then
				local text = string.gsub(entity.Type, "_", " ");
			
				umsg.Start("gms_SetResourceDropInfo", player);
					umsg.String(entity:EntIndex());
					umsg.String(text);
					umsg.Short(entity.Amount);
				umsg.End();
			end;
		end;
	end;
	
	player.Ready = true;
	player:Spawn();
end);

-- Called when the player spawns for the first time.
function GM:PlayerInitialSpawn(player)
	player:SetTeam(1);
end;

function GM:PlayerSpawn(player)
	if (!player.Ready) then return; end;
	
	SetGlobalInt("plantlimit", GetConVarNumber("gms_PlantLimit"))

	if player:HasUnlock("Sprint_Mkii") then
		player:SetWalkSpeed(400)
		player:SetRunSpeed(150)
	else
		player:SetWalkSpeed(250)
		player:SetRunSpeed(250)
	end

	self:PlayerLoadout(player);
	self:PlayerSetModel(player);

	player.Sleepiness = 1000
	player.Hunger = 1000
	player.Thirst = 1000
	if player:HasUnlock("Adept_Survivalist") then player:SetHealth(150) end
	if player:HasUnlock("Master_Survivalist") then player:SetHealth(200) end
	player:UpdateNeeds()
end

function GM:PlayerLoadout(player)
	if (!player.Ready) then return; end;
	
	--Tools
	player:Give("gms_hands")

	if GetConVarNumber("gms_AllTools") == 1 then
		player:Give("gms_stonepickaxe")
		player:Give("gms_copperpickaxe")
		player:Give("gms_ironpickaxe")
		player:Give("gms_stonehatchet")
		player:Give("gms_copperhatchet")
		player:Give("gms_ironhatchet")
		player:Give("gms_woodenfishingrod")
		player:Give("gms_advancedfishingrod")
		player:Give("gms_fryingpan")
		player:Give("gms_shovel")
		player:Give("gms_strainer")
		player:Give("gms_sickle")
		player:Give("gms_woodenspoon")
		player:Give("weapon_crowbar")
		player:Give("weapon_stunstick")
		player:Give("gmod_tool")
		player:Give("weapon_pistol")
		player:Give("gmod_camera")
		player:Give("weapon_smg1")
	end

	--Gmod
	if GetConVarNumber("gms_physgun") == 1 or player:IsAdmin() then player:Give("weapon_physgun") end
	if player:IsAdmin() or player:IsSuperAdmin() then player:Give("gmod_tool") player:Give("pill_pigeon") end
	if player:HasUnlock("Extreme_Survivalist") then player:Give("pill_pigeon") end
	player:Give("weapon_physcannon")
	player:SelectWeapon("gms_hands")
end
	
--Death
function Death(player)
	player.NextSpawnTime = CurTime() + 400
	player:ConCommand( "gms_dropall" )
	if player.AFK == true then
		player:Freeze(false)
		player.AFK = false			
		umsg.Start("gms_stopafk",player)
		umsg.End()
	end
end
hook.Add( "PlayerDeath", "Death", Death )

/*---------------------------------------------------------

  Character saving

---------------------------------------------------------*/
function GM.AutoSaveAllCharacters()
	local GM = GAMEMODE
	SetGlobalInt("plantlimit", GetConVarNumber("gms_PlantLimit"))
	if GetConVarNumber("gms_AutoSave") == 1 then
		for k,v in pairs(player.GetAll()) do
			v:SendMessage("Autosaving..",3,Color(255,255,255,255))
			GM.SaveCharacter(v)
		end
	end
         
	timer.Simple(math.Clamp(GetConVarNumber("gms_AutoSaveTime"),1,60) * 60,GM.AutoSaveAllCharacters)
end

timer.Simple(1,GM.AutoSaveAllCharacters)

-- Called when a player disconnects.
function GM:PlayerDisconnected(player)
	self.SaveCharacter(player);
end;

-- Called when the server shuts down. (Don't think saving players work in this.)
function GM:ShutDown()
	for _, player in pairs(player.GetAll()) do
		self.SaveCharacter(player);
	end;
end;

function GM.SaveCharacter(player)
	local saveTable = {
		skills = player.Skills,
		experience = player.Experience,
		unlocks = player.FeatureUnlocks,
		date = os.date("%A %m/%d/%y")
	};
	
	player:SendMessage("Saved data!", 3, Color(255, 255, 255));
	file.Write("GMStranded/Saves/" .. player:UniqueID() .. ".txt", util.TableToKeyValues(saveTable));
end

function GM.SaveAllCharacters(player)
	if !player:IsAdmin() then 
		player:SendMessage("You need admin rights for this!",3,Color(200,0,0,255))
	return end
         
	for k,v in pairs(player.GetAll()) do
		self.SaveCharacter(v)
	end
         
	player:SendMessage("Saved characters on all current players.",3,Color(255,255,255,255))
end

/*---------------------------------------------------------

  Drop all Command

---------------------------------------------------------*/
function DropAll(player)
	if CurTime() < PlayerIsDropping then return end
	local DeltaTime = 0
	for k,v in pairs(player.Resources) do
		if v > 0  then
			timer.Simple(DeltaTime, function()	player:DecResource(k,v)	player:DropResource(k,v) end, player,k,v)
			DeltaTime = DeltaTime + 0.3			
		end
	end
	PlayerIsDropping = CurTime() + DeltaTime
	player.NextSpawnTime = CurTime() + DeltaTime + 0.5
	
end
concommand.Add( "gms_dropall", DropAll )

/*---------------------------------------------------------

  Salvage Props

---------------------------------------------------------*/
function Salvage(player)
	if player.InProcess then return end
	local tr = player:EyeTrace( 100 )
	if tr.HitNonWorld then
		ent = tr.Entity
	else
		return
	end 

	if ( ent:GetClass() != "gms_buildsite" and ent.NormalProp == true ) then
	 
	local min, max = ent:WorldSpaceAABB()
	-- Do volume in cubic "feet"
	local min, max = ent:OBBMins(), ent:OBBMaxs()
	local vol = math.abs( max.x - min.x ) * math.abs( max.y - min.y ) * math.abs( max.z - min.z )
	vol = vol / ( 16 ^ 3 )
	local res = GMS.MaterialResources[tr.MatType]
	local cost = math.Round( 0.6 * math.ceil( vol * GetConVarNumber( "gms_CostsScale" ) ) )
		player:IncResource( res, cost )
		player:SendMessage( "Gained " ..res.. " (" .. cost .. "x) from salvaging.", 3, Color( 255, 255, 255, 255 ) )
		ent:Fadeout()
	else
		player:SendMessage("Cannot salvage this kind of prop.",5,Color(255,255,255,255))
	end
end
concommand.Add( "gms_salvage", Salvage )

/*---------------------------------------------------------

  Planting

---------------------------------------------------------*/
--Melon planting
function GM.PlantMelon(player,cmd,args)
	if player:GetNWInt("plants") >= GetConVarNumber("gms_PlantLimit") then 
		player:SendMessage("You have hit the plant limit.",3,Color(200,0,0,255))
		return 
	end
	local tr = player:EyeTrace(150)

	if tr.HitWorld then
		if (tr.MatType == MAT_DIRT or tr.MatType == MAT_GRASS or tr.MatType == MAT_SAND) and !GMS.IsInWater(tr.HitPos) then
			if player:GetResource("Melon_Seeds") >= 1 then
				if !GMS.ClassIsNearby(tr.HitPos,"gms_seed",30) and !GMS.ClassIsNearby(tr.HitPos,"prop_physics",50) then
					local data = {}
					data.Pos = tr.HitPos
					player:DoProcess("PlantMelon",3,data)
				else
					player:SendMessage("You need more distance between seeds/props.",3,Color(200,0,0,255))
				end
			else
				player:SendMessage("You need a watermelon seed.",3,Color(200,0,0,255))
			end
		else
			player:SendMessage("You cannot plant on this terrain.",3,Color(200,0,0,255))
		end
	else
		player:SendMessage("Aim at the ground to plant.",3,Color(200,0,0,255))
	end
end
concommand.Add("gms_plantmelon",GM.PlantMelon)

--Banana planting
function GM.PlantBanana(player,cmd,args)
	if player:GetNWInt("plants") >= GetConVarNumber("gms_PlantLimit") then 
		player:SendMessage("You have hit the plant limit.",3,Color(200,0,0,255))
		return 
	end
	local tr = player:EyeTrace(150)

	if tr.HitWorld then
		if (tr.MatType == MAT_DIRT or tr.MatType == MAT_GRASS or tr.MatType == MAT_SAND) and !GMS.IsInWater(tr.HitPos) then
			if player:GetResource("Banana_Seeds") >= 1 then
				if !GMS.ClassIsNearby(tr.HitPos,"gms_seed",30) and !GMS.ClassIsNearby(tr.HitPos,"prop_physics",50) then
					local data = {}
					data.Pos = tr.HitPos
					player:DoProcess("PlantBanana",3,data)
				else
					player:SendMessage("You need more distance between seeds/props.",3,Color(200,0,0,255))
				end
			else
				player:SendMessage("You need a banana seed.",3,Color(200,0,0,255))
			end
		else
			player:SendMessage("You cannot plant on this terrain.",3,Color(200,0,0,255))
		end
	else
		player:SendMessage("Aim at the ground to plant.",3,Color(200,0,0,255))
	end
end
concommand.Add("gms_plantbanana",GM.PlantBanana)

--Orange planting
function GM.PlantOrange(player,cmd,args)
	if player:GetNWInt("plants") >= GetConVarNumber("gms_PlantLimit") then 
		player:SendMessage("You have hit the plant limit.",3,Color(200,0,0,255))
		return 
	end
	local tr = player:EyeTrace(150)

	if tr.HitWorld then
		if (tr.MatType == MAT_DIRT or tr.MatType == MAT_GRASS or tr.MatType == MAT_SAND) and !GMS.IsInWater(tr.HitPos) then
			if player:GetResource("Orange_Seeds") >= 1 then
				if !GMS.ClassIsNearby(tr.HitPos,"gms_seed",30) and !GMS.ClassIsNearby(tr.HitPos,"prop_physics",50) then
					local data = {}
					data.Pos = tr.HitPos
					player:DoProcess("PlantOrange",3,data)
				else
					player:SendMessage("You need more distance between seeds/props.",3,Color(200,0,0,255))
				end
			else
				player:SendMessage("You need an orange seed.",3,Color(200,0,0,255))
			end
		else
			player:SendMessage("You cannot plant on this terrain.",3,Color(200,0,0,255))
		end
	else
		player:SendMessage("Aim at the ground to plant.",3,Color(200,0,0,255))
	end
end
concommand.Add("gms_plantorange",GM.PlantOrange)

--Grain planting
function GM.PlantGrain(player,cmd,args)
	if !player:HasUnlock("Grain_Planting") then
		player:SendMessage("You need more planting skill.",3,Color(200,0,0,255))
	return end
	if player:GetNWInt("plants") >= GetConVarNumber("gms_PlantLimit") then 
		player:SendMessage("You have hit the plant limit.",3,Color(200,0,0,255))
		return 
	end
	local tr = player:EyeTrace(150)

	if tr.HitWorld then
		local nearby = false

		for k,v in pairs(ents.FindInSphere(tr.HitPos,50)) do
			if (v:IsGrainModel() or v:IsProp() or v:GetClass() == "gms_seed") and (tr.HitPos-Vector(v:LocalToWorld(v:OBBCenter()).x,v:LocalToWorld(v:OBBCenter()).y,tr.HitPos.z)):Length() <= 50 then
				nearby = true
			end
		end

		if (tr.MatType == MAT_DIRT or tr.MatType == MAT_GRASS or tr.MatType == MAT_SAND) and !GMS.IsInWater(tr.HitPos) then
			if player:GetResource("Grain_Seeds") >= 1 then
				if !nearby then
					local data = {}
					data.Pos = tr.HitPos
					player:DoProcess("PlantGrain",3,data)
				else
					player:SendMessage("You need more distance between seeds/props.",3,Color(200,0,0,255))
				end
			else
				player:SendMessage("You need a grain seed.",3,Color(200,0,0,255))
			end
		else
			player:SendMessage("You cannot plant on this terrain.",3,Color(200,0,0,255))
		end
	else
		player:SendMessage("Aim at the ground to plant.",3,Color(200,0,0,255))
	end
end
concommand.Add("gms_plantgrain",GM.PlantGrain)

--Berry planting
function GM.PlantBush(player,cmd,args)
	if player:GetNWInt("plants") >= GetConVarNumber("gms_PlantLimit") then 
		player:SendMessage("You have hit the plant limit.",3,Color(200,0,0,255))
		return 
	end
	local tr = player:EyeTrace(150)

	if tr.HitWorld then
		local nearby = false

		for k,v in pairs(ents.FindInSphere(tr.HitPos,50)) do
			if (v:IsBerryBushModel() or v:IsProp() or v:GetClass() == "gms_seed") and (tr.HitPos-Vector(v:LocalToWorld(v:OBBCenter()).x,v:LocalToWorld(v:OBBCenter()).y,tr.HitPos.z)):Length() <= 50  then
				nearby = true
			end
		end

		if (tr.MatType == MAT_DIRT or tr.MatType == MAT_GRASS or tr.MatType == MAT_SAND) and !GMS.IsInWater(tr.HitPos) then
			if player:GetResource("Berries") >= 1 then
				if !nearby then
					local data = {}
					data.Pos = tr.HitPos
					player:DoProcess("PlantBush",3,data)
				else
					player:SendMessage("You need more distance between seeds/props.",3,Color(200,0,0,255))
				end
			else
				player:SendMessage("You need a berry.",3,Color(200,0,0,255))
			end
		else
			player:SendMessage("You cannot plant on this terrain.",3,Color(200,0,0,255))
		end
	else
		player:SendMessage("Aim at the ground to plant.",3,Color(200,0,0,255))
	end
end

concommand.Add("gms_plantbush",GM.PlantBush)


--Tree planting
function GM.PlantTree(player,cmd,args)
	if !player:HasUnlock("Sprout_Planting") then
		player:SendMessage("You need more planting skill.",3,Color(200,0,0,255))
	return end
	local tr = player:EyeTrace(150)

	if tr.HitWorld then 
		if (tr.MatType == MAT_DIRT or tr.MatType == MAT_GRASS or tr.MatType == MAT_SAND) and !GMS.IsInWater(tr.HitPos) then
			if player:GetResource("Sprouts") >= 1 then
				local data = {}
				data.Pos = tr.HitPos
				player:DoProcess("PlantTree",5,data)
			else
				player:SendMessage("You need a sprout.",3,Color(200,0,0,255))
			end
		else
			player:SendMessage("You cannot plant on this terrain.",3,Color(200,0,0,255))
		end
	else
		player:SendMessage("Aim at the ground to plant.",3,Color(200,0,0,255))
	end
end

concommand.Add("gms_planttree",GM.PlantTree)
/*---------------------------------------------------------

  Drink command

---------------------------------------------------------*/
function GM.DrinkFromBottle(player,cmd,args)
	if player:GetResource("Water_Bottles") < 1 then
		player:SendMessage("You need a water bottle.",3,Color(200,0,0,255))
	return end

	player:DoProcess("DrinkBottle",1.5)
end

concommand.Add("gms_DrinkBottle",GM.DrinkFromBottle)

/*---------------------------------------------------------

  Take Medicine command

---------------------------------------------------------*/
function GM.TakeAMedicine(player,cmd,args)
	if player:GetResource("Medicine") < 1 then
		player:SendMessage("You need Medicine.",3,Color(200,0,0,255))
	return end

	player:DoProcess("TakeMedicine",1.5)
end

concommand.Add("gms_TakeMedicine",GM.TakeAMedicine)

/*---------------------------------------------------------

  Drop weapon command

---------------------------------------------------------*/
function GM.DropWeapon(player,cmd,args)
	if !player:Alive() then return end
	if player:GetActiveWeapon():GetClass() == "gms_hands" then
		player:SendMessage("You cannot drop your hands!",3,Color(200,0,0,255))
	elseif player:GetActiveWeapon():GetClass() == "gmod_camera" or player:GetActiveWeapon():GetClass() == "weapon_physgun" or player:GetActiveWeapon():GetClass() == "pill_pigeon" or player:GetActiveWeapon():GetClass() == "weapon_physcannon" then
		player:SendMessage("You cannot drop this!",3,Color(200,0,0,255))
	elseif player:GetActiveWeapon():GetClass() == "weapon_crowbar" then
		player:StripWeapon("weapon_crowbar") 
        local crowbar = ents.Create("weapon_crowbar")
        crowbar:SetPos(player:EyeTrace(100).HitPos + Vector(0,0,15))
        crowbar:Spawn()
	else
		player:DropWeapon(player:GetActiveWeapon())
	end
end

concommand.Add("gms_DropWeapon",GM.DropWeapon)

/*---------------------------------------------------------

  Drop resource command

---------------------------------------------------------*/
function GM.DropResource(player,cmd,args)
	if args == nil or args[1] == nil then
		player:SendMessage("You need to at least give a resource type!",3,Color(200,0,0,255))
	return end
	if CurTime() < PlayerIsDropping then return end

	args[1] = string.Capitalize(args[1])

	if !player.Resources[args[1]] or player.Resources[args[1]] == 0 then
		player:SendMessage("You don't have this kind of resource.",3,Color(200,0,0,255))
	return end

	if args[2] == nil or string.lower(args[2]) == "all" then
		args[2] = tostring(player:GetResource(args[1]))
	end

	if tonumber(args[2]) <= 0 then
		player:SendMessage("No zeros/negatives!",3,Color(200,0,0,255))
	return end

	local int = tonumber(args[2])
	local Type = args[1]

	local res = player:GetResource(Type)

	if int > res then
		int = res
	end
	player:DropResource(Type,int)
	player:DecResource(Type,int)

	player:SendMessage("Dropped "..Type.." ("..int.."x)",3,Color(10,200,10,255))
end

concommand.Add("gms_DropResources",GM.DropResource)

/*---------------------------------------------------------

  Discard resource command

---------------------------------------------------------*/
function GM.DiscardResource(player,cmd,args)
	if args == nil or args[1] == nil then
		player:SendMessage("You need to at least give a resource type!",3,Color(200,0,0,255))
	return end
	if CurTime() < PlayerIsDropping then return end

	args[1] = string.Capitalize(args[1])

	if !player.Resources[args[1]] or player.Resources[args[1]] == 0 then
		player:SendMessage("You don't have this kind of resource.",3,Color(200,0,0,255))
	return end

	if args[2] == nil or string.lower(args[2]) == "all" then
		args[2] = tostring(player:GetResource(args[1]))
	end

	if tonumber(args[2]) <= 0 then
		player:SendMessage("No zeros/negatives!",3,Color(200,0,0,255))
	return end

	local int = tonumber(args[2])
	local Type = args[1]

	local res = player:GetResource(Type)

	if int > res then
		int = res
	end
	player:DecResource(Type,int)

	player:SendMessage("Discarded "..Type.." ("..int.."x)",3,Color(10,200,10,255))
end

concommand.Add("gms_DiscardResources",GM.DiscardResource)

/*---------------------------------------------------------

  Admin Drop resource command

---------------------------------------------------------*/
function GM.ADropResource(player,cmd,args)

	if !player:IsAdmin()	then
		player:SendMessage("You need admin rights for this!",3,Color(200,0,0,255))
	return end

	if args == nil or args[1] == nil then
		player:SendMessage("You need to at least give a resource type!",3,Color(200,0,0,255))
	return end

	args[1] = string.Capitalize(args[1])

	if args[2] == nil or string.lower(args[2]) == "all" then
		args[2] = tostring(player:GetResource(args[1]))
	end

	if tonumber(args[2]) <= 0 then
		player:SendMessage("No zeros/negatives!",3,Color(200,0,0,255))
	return end

	local int = tonumber(args[2])
	local Type = args[1] 

	player:DropResource(Type,int)
	player:SendMessage("Dropped "..Type.." ("..int.."x)",3,Color(10,200,10,255))
end

concommand.Add("gms_ADropResources",GM.ADropResource)

/*---------------------------------------------------------

  Take resource command

---------------------------------------------------------*/

function GM.TakeResource(player,cmd,args)

	if player.InProcess then return end

	if args == nil or args[1] == nil then
		player:SendMessage("You need to at least give a resource type!",3,Color(200,0,0,255))
	return end

	if tonumber(args[2]) <= 0 then
		player:SendMessage("No zeros/negatives!",3,Color(200,0,0,255))
	return end
	args[1] = string.Capitalize(args[1])

	local tr = player:EyeTrace(150)
	local ent = tr.Entity
	local cls = tr.Entity:GetClass()
	if cls != "gms_resourcedrop" or (player:GetPos()-ent:LocalToWorld(ent:OBBCenter())):Length() >= 65 or ent.Type != args[1] then return end

	local int = tonumber(args[2])
	local room = player.MaxResources - player:GetAllResources()

	if int >= ent.Amount then
		int = ent.Amount
	end

	if room <= 0 then
		player:SendMessage("You can't carry anymore!",3,Color(200,0,0,255))
	return end

	if room < int then
		int = room
	end

	ent.Amount = ent.Amount - int

	if ent.Amount <= 0 then
		ent:Fadeout()
	else
		ent:ApplyDropInfo()
	end

	player:IncResource(ent.Type,int)
	player:SendMessage("Picked up "..ent.Type.." ("..int.."x)",4,Color(10,200,10,255))
end

concommand.Add("gms_TakeResources",GM.TakeResource)

/*---------------------------------------------------------

  Buildings menu

---------------------------------------------------------*/
function GM.OpenBuildingsCombi(player)
	player:OpenCombiMenu("Buildings")
end

concommand.Add("gms_BuildingsCombi",GM.OpenBuildingsCombi)
/*---------------------------------------------------------

  Generic combi menu

---------------------------------------------------------*/
function GM.OpenGenericCombi(player)
	player:OpenCombiMenu("Generic")
end

concommand.Add("gms_GenericCombi",GM.OpenGenericCombi)
/*---------------------------------------------------------

  Make combination

---------------------------------------------------------*/
function GM.MakeCombination(player,cmd,args)
	if !args[1] or !args[2] then
		player:SendMessage("Please specify a valid combination.",3,Color(255,255,255,255))
		player:CloseCombiMenu()
	return end

	local group = args[1]
	local combi = args[2]

	if !GMS.Combinations[group] then return end
	if !GMS.Combinations[group][combi] then return end

	local tbl = GMS.Combinations[group][combi]

	
	--Check for nearby forge/fire etc:
	if group == "Cooking" then
		local nearby = false

		for k,v in pairs(ents.FindInSphere(player:GetPos(),100)) do
			if (v:IsProp() and v:IsOnFire()) or v:GetModel() == "models/props_c17/furniturestove001a.mdl" then nearby = true end
		end

		if !nearby then
			player:SendMessage("You need to be close to a fire!",3,Color(200,0,0,255))
			player:CloseCombiMenu()
		return end

	elseif group == "StoneWeapons" then
		local nearby = false

		for k,v in pairs(ents.FindInSphere(player:GetPos(),100)) do
			if v:GetClass() == "gms_stoneworkbench" then nearby = true end
		end

		if !nearby then
			player:SendMessage("You need to be close to a workbench!",3,Color(200,0,0,255))
			player:CloseCombiMenu()
		return end
		
	elseif group == "CopperWeapons" then
		local nearby = false

		for k,v in pairs(ents.FindInSphere(player:GetPos(),100)) do
			if v:GetClass() == "gms_copperworkbench" then nearby = true end
		end

		if !nearby then
			player:SendMessage("You need to be close to a workbench!",3,Color(200,0,0,255))
			player:CloseCombiMenu()
		return end
		
	elseif group == "IronWeapons" then
		local nearby = false

		for k,v in pairs(ents.FindInSphere(player:GetPos(),100)) do
			if v:GetClass() == "gms_ironworkbench" then nearby = true end
		end

		if !nearby then
			player:SendMessage("You need to be close to a workbench!",3,Color(200,0,0,255))
			player:CloseCombiMenu()
		return end
		
	elseif group == "StoneFurnace" then
		local nearby = false

		for k,v in pairs(ents.FindInSphere(player:GetPos(),100)) do
			if v:GetClass() == "gms_stonefurnace" then nearby = true end
		end

		if !nearby then
			player:SendMessage("You need to be close to a furnace!",3,Color(200,0,0,255))
			player:CloseCombiMenu()
		return end
		
	elseif group == "CopperFurnace" then
		local nearby = false

		for k,v in pairs(ents.FindInSphere(player:GetPos(),100)) do
			if v:GetClass() == "gms_copperfurnace" then nearby = true end
		end

		if !nearby then
			player:SendMessage("You need to be close to a furnace!",3,Color(200,0,0,255))
			player:CloseCombiMenu()
		return end
		
	elseif group == "IronFurnace" then
		local nearby = false

		for k,v in pairs(ents.FindInSphere(player:GetPos(),100)) do
			if v:GetClass() == "gms_ironfurnace" then nearby = true end
		end

		if !nearby then
			player:SendMessage("You need to be close to a furnace!",3,Color(200,0,0,255))
			player:CloseCombiMenu()
		return end
	end		 

	--Check for skills
	local numreq = 0

	if tbl.SkillReq then
		for k,v in pairs(tbl.SkillReq) do
			if player:GetSkill(k) >= v then
				numreq = numreq + 1
			end
		end

		if numreq < table.Count(tbl.SkillReq) then
			player:SendMessage("Not enough skill.",3,Color(200,0,0,255))
			player:CloseCombiMenu()
		return end
	end



	--Check for resources
	local numreq = 0

	for k,v in pairs(tbl.Req) do
		if player:GetResource(k) >= v then
			numreq = numreq + 1
		end
	end

	if numreq < table.Count(tbl.Req) and group != "Buildings" then
		player:SendMessage("Not enough resources.",3,Color(200,0,0,255))
		player:CloseCombiMenu()
	return end

	player:CloseCombiMenu()
	
	--All well, make stuff:
	if group == "Cooking" then
		local data = {}
		data.Name = tbl.Name
		data.FoodValue = tbl.FoodValue
		data.Cost = table.Copy(tbl.Req)
		local time = 5

		if player:GetActiveWeapon():GetClass() == "gms_fryingpan" then
			time = 2
		end

		player:DoProcess("Cook",time,data)

	elseif group == "Generic" then
		local data = {}
		data.Name = tbl.Name
		data.Res = tbl.Results
		data.Cost = table.Copy(tbl.Req)
		local time = 5

		player:DoProcess("MakeGeneric",time,data)

	elseif group == "Factory" or group == "Gunmaking" or group == "Utilities" then
		local data = {}
		data.Name = tbl.Name
		if tbl.AllSmelt == true then
			local sourcetable = player:AllSmelt(tbl)
			data.Res  = sourcetable.Results
			data.Cost = table.Copy(sourcetable.Req)
		else		
			data.Res = tbl.Results
			data.Cost = table.Copy(tbl.Req)
		end
		local timecount = 1
		for k, v in pairs(data.Cost) do
			timecount  = timecount + v
		end 
		local time = timecount * 0.3

		if tbl.SwepClass != nil then
			data.Class = tbl.SwepClass
			player:DoProcess("MakeWeapon",time,data)
		else		
			player:DoProcess("Processing",time,data)
		end

	elseif group == "StoneWeapons" then
		local data = {}
		data.Name = tbl.Name
		data.Class = tbl.SwepClass
		data.Cost = table.Copy(tbl.Req)
		local time = 10

		player:DoProcess("MakeWeapon",time,data)

	elseif group == "CopperWeapons" then
		local data = {}
		data.Name = tbl.Name
		data.Class = tbl.SwepClass
		data.Cost = table.Copy(tbl.Req)
		local time = 10

		player:DoProcess("MakeWeapon",time,data)

	elseif group == "IronWeapons" then
		local data = {}
		data.Name = tbl.Name
		data.Class = tbl.SwepClass
		data.Cost = table.Copy(tbl.Req)
		local time = 10

		player:DoProcess("MakeWeapon",time,data)			

	elseif group == "Buildings" then
		local data = {}
		local trs = {}
		data.Name = tbl.Name
		data.Class = tbl.Results
		data.Cost = table.Copy(tbl.Req) 
		local time = 20
		data.BuildSiteModel = tbl.BuildSiteModel
		trs = player:EyeTrace(250)
		if !trs.HitWorld then
			player:SendMessage("Aim at the ground to construct a structure.",3,Color(200,0,0,255))
		return end

		player:DoProcess("MakeBuilding",time,data)

	elseif group == "StoneFurnace" then
		local data = {}
		data.Name = tbl.Name
		if tbl.AllSmelt == true then
			local sourcetable = player:AllSmelt(tbl)
			data.Res  = sourcetable.Results
			data.Cost = table.Copy(sourcetable.Req)
		else		
			data.Res = tbl.Results
			data.Cost = table.Copy(tbl.Req)
		end
		local timecount = 1
		for k, v in pairs(data.Cost) do
			timecount  = timecount + v
		end 
		local time = timecount * 0.5

		player:DoProcess("Smelt",time,data)

	elseif group == "CopperFurnace" then
		local data = {}
		data.Name = tbl.Name
		if tbl.AllSmelt == true then
			local sourcetable = player:AllSmelt(tbl)
			data.Res  = sourcetable.Results
			data.Cost = table.Copy(sourcetable.Req)
		else		
			data.Res = tbl.Results
			data.Cost = table.Copy(tbl.Req)
		end
		local timecount = 1
		for k, v in pairs(data.Cost) do
			timecount  = timecount + v
		end 
		local time = timecount * 0.6

		player:DoProcess("Smelt",time,data)	

	elseif group == "IronFurnace" then
		local data = {}
		data.Name = tbl.Name
		if tbl.AllSmelt == true then
			local sourcetable = player:AllSmelt(tbl)
			data.Res  = sourcetable.Results
			data.Cost = table.Copy(sourcetable.Req)
		else		
			data.Res = tbl.Results
			data.Cost = table.Copy(tbl.Req)
		end
		local timecount = 1
		for k, v in pairs(data.Cost) do
			timecount  = timecount + v
		end 
		local time = timecount * 0.7

		player:DoProcess("Smelt",time,data)				

	elseif group == "GrindingStone" then
		local data = {}
		data.Name = tbl.Name
		data.Res = tbl.Results
		data.Cost = table.Copy(tbl.Req)
		local timecount = 1
		for k, v in pairs(data.Cost) do
			timecount  = timecount + v
		end 
		local time = timecount * 0.75

		player:DoProcess("Crush",time,data)

	end
end

concommand.Add("gms_MakeCombination",GM.MakeCombination)

-- A function to pickup objects with the physgun.
function GM:PhysgunPickup(player, entity)
	if (player:IsAdmin()) then return true; end;
	
	local owner = entity.owner;

	if (table.HasValue(GMS.PickupProhibitedClasses, entity:GetClass()) or entity:IsPlayer()) then return false; end;
	if (owner != nil and owner == "World" or !player:IsBuddy(owner) and owner != player) then return false; end;

	return true;
end;

-- A function to punt an object with the gravgun.
function GM:GravGunPunt(player, entity)
	return player:IsAdmin();
end;

function GM:CanTool(player, tr, mode)
	local ent = tr.Entity

	if mode == "remover" then
		if !player:IsAdmin() then
			if (ent:IsFoodModel() or ent:IsTreeModel() or ent:IsRockModel() or table.HasValue(GMS.PickupProhibitedClasses,ent:GetClass())) then
				player:SendMessage("This is prohibited!",3,Color(200,0,0,255))
				return false
			end
		end
	end
	
	if mode == "rope" then
		if player:GetResource("Rope") < 1 then
			player:SendMessage("You need rope to use this tool.",3,Color(200,0,0,255))
			return false
		end
	end
	
	if mode == "weld" then
		if player:GetResource("Welder") < 1 then
			player:SendMessage("You need a Welder to use this tool.",3,Color(200,0,0,255))
			return false
		end
	end

	if table.HasValue(GMS.ProhibitedStools,mode) then
		player:SendMessage("This sTOOL is prohibited.",3,Color(200,0,0,255))
		return false
	end

	return true
end

-- A functoin to spawn a prop.
function GM:PlayerSpawnedProp(player, model, entity)
	if (GetConVarNumber("gms_FreeBuild") == 1 or player.InProcess) then return false; end;

	if (!player.CanSpawnProp) then
		player:SendMessage("No spamming!", 3, Color(200, 0, 0));
		return false;
	end;

	entity.owner = player;
	entity.NormalProp = true;
	
	player.CanSpawnProp = false;
	timer.Simple(0.2, self.PlayerSpawnedPropDelay, self, player, model, entity);
end;

function GM:PlayerSpawnedPropDelay(player, mdl, ent)
	player.CanSpawnProp = true
	if player.InProcess then return end

	--Admin only models
	if (ent:IsRockModel() or ent:IsTreeModel() or ent:IsFoodModel()) and !player:IsAdmin() then
		ent:Remove()
		player:SendMessage("You cannot spawn this prop unless you're admin.",5,Color(255,255,255,255))
	return end

	--Trace
	ent.EntOwner = player
	local min,max = ent:WorldSpaceAABB()

	-- Do volume in cubic "feet"
	local min, max = ent:OBBMins(), ent:OBBMaxs()
	local vol = math.abs(max.x-min.x) * math.abs(max.y-min.y) * math.abs(max.z-min.z)
	vol = vol/(16^3)

	local x = 0
	local trace = nil
	local tr = nil
	trace = {}
	trace.start = ent:GetPos() + Vector((math.random()*200)-100,(math.random()*200)-100,(math.random()*200)-100)
	trace.endpos = ent:GetPos()
	tr = util.TraceLine(trace)

	while (tr.Entity != ent and x < 5) do
		x = x + 1

		trace = {}
		trace.start = ent:GetPos() + Vector((math.random()*200)-100,(math.random()*200)-100,(math.random()*200)-100)
		trace.endpos = ent:GetPos()

		tr = util.TraceLine(trace)
	end

	--Faulty trace
	if tr.Entity != ent then
		ent:Remove()
		player:SendMessage("You need more space to spawn.",3,Color(255,255,255,255))
	return end

	local res = GMS.MaterialResources[tr.MatType]
	local cost = math.ceil(vol*GetConVarNumber("gms_CostsScale"))

	if cost > player:GetResource(res) then
		if player:GetBuildingSite() and player:GetBuildingSite():IsValid() then player:GetBuildingSite():Remove() end
		local site = player:CreateBuildingSite(ent:GetPos(),ent:GetAngles(),ent:GetModel(),ent:GetClass())
		local tbl = site:GetTable()
		site.EntOwner = player
		site.NormalProp = true
		local costtable = {}
		costtable[res] = cost

		tbl.Costs = table.Copy(costtable)
		player:DoProcess("Assembling",2)
		player:SendMessage("Not enough resources, creating buildsite.",3,Color(255,255,255,255))
		local str = ":"
		for k,v in pairs(site.Costs) do
			str = str.." "..k.." ("..v.."x)"
		end
		site:SetNetworkedString('Resources', str)
		local Name = "Prop"
		site:SetNetworkedString('Name', Name)
		ent:Remove()
		return
	end

	--Resource cost
	if player:GetResource(res) < cost then
		ent:Remove()
		player:SendMessage("You need "..res.." ("..cost.."x) to spawn this prop.",3,Color(200,0,0,255))
	else
		player:DecResource(res,cost)
		player:SendMessage("Used "..res.." ("..cost.."x) to spawn this prop.",3,Color(255,255,255,255))
		player:DoProcess("Assembling",5)
	end
end

function GM:PlayerSpawnedEffect(player,mdl,ent)
	if GetConVarNumber("gms_FreeBuild") == 1 then return end
	ent:Remove()
end

function GM:PlayerSpawnedNPC(player,ent)
	if GetConVarNumber("gms_FreeBuild") == 1 then return end
	ent:Remove()
end

function GM:PlayerSpawnedVehicle(player,ent)
	if GetConVarNumber("gms_FreeBuild") == 1 then return end
	ent:Remove()
end

function GM:PlayerSpawnedSENT( player, prop )
	if GetConVarNumber("gms_AllowSENTSpawn") == 1 then return end
	prop:Remove()
end

function GM:Think()
	self.ProcessThink();
	
	for k,tbl in pairs(self.RisingProps) do
		if tbl.Entity:GetPos().z >= tbl.Origin then
			table.remove(self.RisingProps,k)
		else
			tbl.Entity:SetPos(tbl.Entity:GetPos() + Vector(0,0,1*tbl.Speed))
		end
	end

	for k,tbl in pairs(self.SinkingProps) do
		if tbl.Entity:GetPos().z <= tbl.Origin - tbl.Height then
			table.remove(self.SinkingProps,k)
			tbl.Entity:Remove()
		else
			tbl.Entity:SetPos(tbl.Entity:GetPos() + Vector(0,0,-1*tbl.Speed))
		end
	end
	
	for k,ent in pairs(GMS.FadingInProps) do
		if !ent or ent == NULL then
			table.remove(GMS.FadingInProps,k)
		elseif !ent:IsValid() then
			table.remove(GMS.FadingInProps,k)
		elseif ent.AlphaFade >= 255 then
			table.remove(GMS.FadingInProps,k)
		else
			ent.AlphaFade = ent.AlphaFade + 1
			ent:SetColor(255,255,255,ent.AlphaFade)
		end
	end
	
	for _, v in ipairs(ents.GetAll()) do
		if v:WaterLevel() > 0 and v:IsOnFire() then
			v:Extinguish()
		end 
	end
end;

/*---------------------------------------------------------
  SWEP Spawning
---------------------------------------------------------*/
--Override
function CCSpawnSWEP( player, command, arguments )
	if GetConVarNumber("gms_AllowSWEPSpawn") == 0 then
		player:SendMessage("SWEP spawning is disabled.",3,Color(200,0,0,255))
	return end

	if ( arguments[1] == nil ) then return end 

	-- Make sure this is a SWEP 
	local swep = weapons.GetStored( arguments[1] ) 
	if (swep == nil) then return end 

	-- You're not allowed to spawn this! 
	if ( !swep.Spawnable && !player:IsAdmin() ) then return end 

	MsgAll( "Giving "..player:Nick().." a "..swep.Classname.."\n" ) 
	player:Give( swep.Classname ) 

 end 
   
 concommand.Add( "gm_giveswep", CCSpawnSWEP )
/*---------------------------------------------------------

  Needs

---------------------------------------------------------*/
function PlayerMeta:UpdateNeeds()
	umsg.Start("gms_setneeds",self)
		umsg.Short(self.Sleepiness)
		umsg.Short(self.Hunger)
		umsg.Short(self.Thirst)
	umsg.End()
end

timer.Create("gms_SubtractNeeds", 1, 0, function()
	for k, player in pairs(player.GetAll()) do
		if (!player.Ready) then return; end;

		if player:Alive() then
			--Sleeping
			if player.Sleeping then
				if player.Sleepiness < 1000 and player.Sleepiness <= 950 then
					local trace = {}
					trace.start = player:GetShootPos()
					trace.endpos = trace.start - (player:GetUp() * 300)
					trace.filter = player

					local tr = util.TraceLine(trace)
					if Entity(tr.Entity) and tr.Entity:IsSleepingFurniture() then
						player.Sleepiness = player.Sleepiness + 100
					else
						player.Sleepiness = player.Sleepiness + 50
					end
				elseif player.Sleepiness > 950 then
					player.Sleepiness = 1000
					GAMEMODE.PlayerWake(player)
				end

				if player.Thirst - 20 < 0 then
					player.Thirst = 0
				else
					player.Thirst = player.Thirst - 20
				end

				if player.Hunger - 20 < 0 then
					player.Hunger = 0
				else
					player.Hunger = player.Hunger - 20
				end

				if player.NeedShelter then player:SetHealth(player:Health() - 10) end
			end

			--Kay you're worn out
			--if player.AFK != true then
				if (player.Sleepiness > 0) then
					player.Sleepiness = player.Sleepiness -1
				end;
				
				if (player.Thirst > 0) then
					player.Thirst = player.Thirst -3
				end;
				
				if (player.Hunger > 0) then
					player.Hunger = player.Hunger -1
				end;
			--end;
    
			player:UpdateNeeds()

			--Are you dying?
			if player.Sleepiness <= 0 or player.Thirst <= 0 or player.Hunger <= 0 then
				if player:Health() > 4 then
					player:SetHealth(player:Health() - 2)
				else
					player:Kill()
				end;
			end;
		end;
	end;
end);

/*---------------------------------------------------------
  Sleep
---------------------------------------------------------*/
function GM.PlayerSleep(player,cmd,args)
	if player.Sleeping or !player:Alive() or player.AFK == true then return end
	if player.Sleepiness > 700 then
		player:SendMessage("You're not tired enough.",3,Color(255,255,255,255))
	return end

	player.Sleeping = true
	umsg.Start("gms_startsleep",player)
	umsg.End()

	player:Freeze(true)

	--Check for shelter
	local trace = {}
	trace.start = player:GetShootPos()
	trace.endpos = trace.start + (player:GetUp() * 300)
	trace.filter = player

	local tr = util.TraceLine(trace)

	if !tr.HitWorld and !tr.HitNonWorld then
		player.NeedShelter = true
	else
		player.NeedShelter = false
	end
end

concommand.Add("gms_sleep",GM.PlayerSleep)

function GM.PlayerWake(player,cmd,args)
	if !player.Sleeping then return end
	player.Sleeping = false
	umsg.Start("gms_stopsleep",player)
	umsg.End()

	player:Freeze(false)

	--Check for shelter
	local trace = {}
	trace.start = player:GetShootPos()
	trace.endpos = trace.start + (player:GetUp() * 300)
	trace.filter = player

	local tr = util.TraceLine(trace)

	if player.NeedShelter then
		player:SendMessage("You should find some shelter!",5,Color(200,0,0,255))
	else
		player:SendMessage("Ah, nothing like a good nights sleep!",5,Color(255,255,255,255))
	end
end

concommand.Add("gms_wakeup",GM.PlayerWake)

/*---------------------------------------------------------
 AFK
---------------------------------------------------------*/

function GM.AFK(player,cmd,args)

	if player.Sleeping or !player:Alive() then return end
	if player.InProcess then return end
	if player.AFK == false or player.AFK == nil then
		player:SetNWString( "AFK", 1 )
		player.AFK = true			
		umsg.Start("gms_startafk",player)
		umsg.End()
	else
		player:SetNWString( "AFK", 0 )
		player.AFK = false			
		umsg.Start("gms_stopafk",player)
		umsg.End()
	end

	player:Freeze(player.AFK)

end

concommand.Add("gms_afk",GM.AFK)

/*---------------------------------------------------------
  Player Stuck
---------------------------------------------------------*/
function GM.PlayerStuck(player,cmd,args)
	if player.InProcess then return end

	if player.Spam == true then
		player:SendMessage("No spamming!",3,Color(200,0,0,255))
	return end

	if player.Spam == false or player.Spam == nil then
		player:SetPos( player:GetPos() + Vector(0,0,64) )
	end

	player.Spam = true
	timer.Simple(0.2, function() player.Spam = false end, player)
end

concommand.Add("gms_stuck",GM.PlayerStuck)

/*---------------------------------------------------------
  Loot-Able npcs
---------------------------------------------------------*/
GMS.LootableNPCs = 
{
	"npc_antlion",
    "npc_antlionguard",
	"npc_crow",
	"npc_seagull",
	"npc_pigeon"
}

function EntityMeta:IsLootableNPC()
	if table.HasValue(GMS.LootableNPCs,self:GetClass()) then
		return true
	end

	return false
end

function GM:OnNPCKilled( npc, killer, weapon )
	if npc:IsLootableNPC() then
		if killer:IsPlayer() then
			local loot = ents.Create("gms_loot")
			local tbl = loot:GetTable()
			local num = math.random(1,3)

			local res = {}
			--SPropProtection.PlayerMakePropOwner(killer , loot)
			res["Meat"] = num
			tbl.Resources = res
			loot:SetPos(npc:GetPos() + Vector(0,0,64))
			loot:Spawn()
			timer.Simple(180, function(loot) if loot then loot:Remove() end end, loot)
			npc:Fadeout(5)
		else
			npc:Fadeout(5)			
		end
	end
end

/*---------------------------------------------------------
  Make Campfire command
---------------------------------------------------------*/
GM.CampFireProps = {}

function GM.CampFireTimer()
	if GetConVarNumber("gms_Campfire") == 1 then
		local GM = GAMEMODE

		for k,ent in pairs(GM.CampFireProps) do
			if !ent or ent == NULL then
				table.remove(GM.CampFireProps,k)
			else
				if CurTime() - ent.CampFireLifeTime >= 180 then
					ent:Fadeout()
					table.remove(GM.CampFireProps,k)
				elseif ent:WaterLevel() > 0 then
					ent:Fadeout()
					table.remove(GM.CampFireProps,k)
				else	
					ent:SetHealth(ent.CampFireMaxHP)
				end
			end
		end
		timer.Simple(1,GM.CampFireTimer)
	end
end

timer.Simple(1,GM.CampFireTimer)

function GM.MakeCampfire(player,cmd,args)
	if GetConVarNumber("gms_Campfire") == 1 then
		local tr = player:EyeTrace(150)

		if !tr.HitNonWorld or !tr.Entity then
			player:SendMessage("Aim at the prop(s) to use for campfire.",3,Color(255,255,255,255))
		return end

		local ent = tr.Entity
		local cls = tr.Entity:GetClass()

		if ent:IsOnFire() or cls != "prop_physics" and cls != "prop_physics_multiplayer" and cls != "prop_dynamic" then
			player:SendMessage("Aim at the prop(s) to use for campfire.",3,Color(255,255,255,255))
		return end

		local mat = tr.MatType

		if player:GetResource("Wood") < 5 then
			player:SendMessage("You need at least 5 wood to make a fire.",5,Color(255,255,255,255))
		return end

		if mat != MAT_WOOD then
			player:SendMessage("Prop has to be wood, or if partially wood, aim at the wooden part.",5,Color(255,255,255,255))
		return end

		local data = {}
		data.Entity = ent
		
		--if (SPropProtection.PlayerIsPropOwner(player, ent) or SPropProtection.IsBuddy(player, ent)) or tonumber(SPropProtection["Config"]["use"]) != 1 then		
			player:DoProcess("Campfire",5,data)
		--return end

	end
end

concommand.Add("gms_makefire",GM.MakeCampfire)

function EntityMeta:MakeCampfire()
	if GetConVarNumber("gms_Campfire") == 1 then
		self:Ignite(180,0)

		self.CampFireMaxHP = self:Health()
		self.CampFireLifeTime = CurTime()

		table.insert(GAMEMODE.CampFireProps,self)

		local mdl = self:GetModel()

		for k,v in pairs(ents.FindInSphere(self:GetPos(),100)) do
			local cls = v:GetClass()
			if v != ent and v:IsProp() then
				if v:GetModel() == mdl then
					GetConVarNumber("gms_MaxReproducedTrees")
					if GetConVarNumber("gms_SpreadFire") == 1 then
						v:Ignite(180,(v:OBBMins()-v:OBBMaxs()):Length()*0.50+10)
					else
						v:Ignite(180,0)
					end

					v.CampFireMaxHP = v:Health()
					v.CampFireLifeTime = CurTime()

					table.insert(GAMEMODE.CampFireProps,v)
				end
			end
		end
	end
end

-- Called when the player presses a key.
function GM:KeyPress(player, key)
	if (key == IN_SPEED) then
		if (player:HasUnlock("Sprint_Mki") and !player:HasUnlock("Sprint_Mkii")) then
			self:SetPlayerSpeed(player, 250, 400);
		end;
	end;
	
	if (key != IN_USE or player:KeyDown(1)) then return; end;

	local tr = player:EyeTrace(150);
	
	if (tr.HitNonWorld) then
		if (tr.Entity and !GMS.IsInWater(tr.HitPos)) then
			local entity = tr.Entity;
			local model = tr.Entity:GetModel();
			local class = tr.Entity:GetClass();

			if ((entity:IsFoodModel() or class == "gms_food") and (player:GetPos() -entity:LocalToWorld(entity:OBBCenter())):Length() <= 65) then
				if (class == "gms_food") then
					player:SendMessage("Restored " .. tostring((entity.Value /1000) *100) .. "% food.", 3 ,Color(10, 200, 10));
					player:SetFood(player.Hunger +entity.Value);
					
					entity:Fadeout(2);
					
					player:Heal(entity.Value /20);
					player:SendMessage("Regained " .. tostring(entity.Value /20) .. " health.", 3, Color(255, 0, 0));
				else
					player:DoProcess("EatFruit", 2, {Entity = entity});
				end;
			elseif (entity:IsTreeModel()) then
				if (!player:HasUnlock("Sprout_Collect")) then
					player:SendMessage("You don't have enough skill.", 3, Color(200, 0, 0));
					return;
				end;
			
				player:DoProcess("SproutCollect", 5);
			elseif (class == "gms_resourcedrop" and (player:GetPos() -tr.HitPos):Length() <= 80) then
				player:PickupResourceEntity(entity);
			elseif (entity:IsOnFire() and GetConVarNumber("gms_CampFire") == 1) then
				player:OpenCombiMenu("Cooking");
			end;
		end;
	elseif (tr.HitWorld) then
		for k, v in pairs(ents.FindInSphere(tr.HitPos, 100)) do
			if (v:IsGrainModel()) then
				player:DoProcess("HarvestGrain", 3, {Entity = v});
				return;
			elseif (v:IsBerryBushModel()) then
				player:DoProcess("HarvestBush", 3, {Entity = v});
				return;
			end;
		end;

		if ((tr.MatType == MAT_DIRT or tr.MatType == MAT_GRASS or tr.MatType == MAT_SAND or tr.MatType == MAT_SNOW) and !GMS.IsInWater(tr.HitPos)) then
			local duration = 5;
			
			if (player:GetActiveWeapon():GetClass() == "gms_shovel") then
				duration = 2;
			end;

			player:DoProcess("Foraging", duration);
		end;
	end;
	
	local trWater = {};
	trWater.start = player:GetShootPos();
	trWater.endpos = trWater.start +(player:GetAimVector() *150);
	trWater.mask = MASK_WATER | MASK_SOLID;
	trWater.filer = player;

	local trace = util.TraceLine(trWater);
	if ((trace.Hit and trace.MatType == MAT_SLOSH and player:WaterLevel() > 0) or player:WaterLevel() == 3) then
		if (player.Thirst < 1000 and player.Thirst > 950) then
			player.Thirst = 1000;
			player:UpdateNeeds();
			
			if (player.drinkDelay < CurTime()) then
				player:EmitSound("npc/barnacle/barnacle_gulp" .. math.random(1, 2) .. ".wav");
				player.drinkDelay = CurTime() +0.9;
			end;
		elseif (player.Thirst < 950) then
			player.Thirst = player.Thirst +50;
			
			if (player.drinkDelay < CurTime()) then
				player:EmitSound("npc/barnacle/barnacle_gulp" .. math.random(1, 2) .. ".wav");
				player.drinkDelay = CurTime() +0.9;
			end;
			
			player:UpdateNeeds();
		end;
	elseif (GMS.IsInWater(tr.HitPos) and !tr.HitNonWorld) then
		player:DoProcess("BottleWater", 3);
	end;
end;

function PlayerMeta:PickupResourceEntity(entity)
	local amount = entity.Amount;
	local room = self.MaxResources -self:GetAllResources();

	if (room <= 0) then
		self:SendMessage("You can't carry anymore!", 3, Color(200, 0, 0));
		return;
	end;

	if (room < amount) then
		amount = room;
	end;

	entity.Amount = entity.Amount -amount;

	if (entity.Amount <= 0) then
		entity:Fadeout();
	else
		entity:ApplyDropInfo();
	end;

	self:IncResource(entity.Type, amount)
	self:SendMessage("Picked up " .. entity.Type .. " (x" .. amount .. ")", 4, Color(10, 200, 10));
end;

hook.Add("KeyReleased", "gms_sprinthook", function(player, key)
	if (key == IN_SPEED and player:Alive()) then
		if (player:HasUnlock("Sprint_Mki") and !player:HasUnlock("Sprint_Mkii")) then
			GAMEMODE:SetPlayerSpeed(player, 250, 250);
		end;
	end;
end);

/*---------------------------------------------------------
  Tribe: The Stranded
---------------------------------------------------------*/
function GM.Tribe1( player )
	player:SetTeam(1)
	player:SendMessage("Successfully changed to Tribe: The Stranded!",5,Color(255,255,255,255))
end

concommand.Add("gms_tribe1", GM.Tribe1)
/*---------------------------------------------------------
  Tribe: Scavengers
---------------------------------------------------------*/
function GM.Tribe2( player )
	player:SetTeam(24)
	player:SendMessage("Successfully changed to Tribe: Scavengers",5,Color(255,255,255,255))
end

concommand.Add("gms_tribe2", GM.Tribe2)
/*---------------------------------------------------------
  Tribe: The Dynamics
---------------------------------------------------------*/
function GM.Tribe3( player )
	player:SetTeam(23)
	player:SendMessage("Successfully changed to Tribe: The Dynamics",5,Color(255,255,255,255))
end

concommand.Add("gms_tribe3", GM.Tribe3)
/*---------------------------------------------------------
  Tribe: The Gummies
---------------------------------------------------------*/
function GM.Tribe4( player )
	player:SetTeam(22)
	player:SendMessage("Successfully changed to Tribe: The Gummies",5,Color(255,255,255,255))
end

concommand.Add("gms_tribe4", GM.Tribe4)
/*---------------------------------------------------------
  Tribe: Anonymous
---------------------------------------------------------*/
function GM.Tribe5( player )
	player:SetTeam(21)
	player:SendMessage("Successfully changed to Tribe: Anonymous",5,Color(255,255,255,255))
end

concommand.Add("gms_tribe5", GM.Tribe5)
/*---------------------------------------------------------
  Tribe: Survivalists
---------------------------------------------------------*/
function GM.Tribe6( player )
	player:SetTeam(20)
	player:SendMessage("Successfully changed to Tribe: Survivalists",5,Color(255,255,255,255))
end
concommand.Add("gms_tribe6", GM.Tribe6) 

local alertSound = {
	"citizen_beaten1.wav",
	"citizen_beaten4.wav",
	"citizen_beaten5.wav",
	"cough1.wav",
	"cough2.wav",
	"cough3.wav",
	"cough4.wav"
};

-- Alert timer for needs.
timer.Create("gms_needsAlert", 5, 0, function()
	for _, player in pairs(player.GetAll()) do
		if (!player.Ready) then return; end;
		
		if (player:Alive() and player.Thirst < 125 or player.Sleepiness < 125 or player.Hunger < 125) then
			player:EmitSound(alertSound[math.random(1, #alertSound)]);
		end;
	end;
end);

/*---------------------------------------------------------
   Tribe system
---------------------------------------------------------*/
include( 'chatcommands.lua' )
function GM:PlayerSay(player,text,public)
	if (string.find(text,"/createtribe") == 1) then
		local stuff = string.Explode("\"",text)
		if !stuff[2] or stuff[2] == "" then
			args = string.Explode(" ",text)
			if !args[5] then player:ChatPrint("Syntax is: /createtribe \"tribename\" red green blue [password(optional)]") return "" end
		else
			args1 = string.Explode(" ",stuff[1])
			args2 = string.Explode(" ",stuff[3])
			if !args2[3] then player:ChatPrint("Syntax is: /createtribe \"tribename\" red green blue [password(optional)]") return "" end
		end
		if stuff[2] and stuff[2] != "" then
			name = stuff[2]
			for i=2,4 do
				if !args2[i] or !tonumber(args2[i]) then player:ChatPrint("Syntax is: /createtribe \"tribename\" red green blue [password(optional)]") return "" end
			end
		else
			name = args[2]
			for i=3,5 do
				if !args[i] or !tonumber(args[i]) then player:ChatPrint("Syntax is: /createtribe \"tribename\" red green blue [password(optional)]") return "" end
			end
		end
		if stuff[2] and stuff[2] != "" then
			red = math.Clamp(tonumber(args2[2]),0,255)
			green = math.Clamp(tonumber(args2[3]),0,255)
			blue = math.Clamp(tonumber(args2[4]),0,255)
		else
			red = math.Clamp(tonumber(args[3]),0,255)
			green = math.Clamp(tonumber(args[4]),0,255)
			blue = math.Clamp(tonumber(args[5]),0,255)
		end
		local Password = false
		if stuff[2] and stuff[2] != "" then
			if args2[5] and args2[5] != "" then
				Password = args2[5]
			end
		else
			if args[6] and args[6] != "" then
				Password = args[6]
			end
		end
		
		GAMEMODE.NumTribes = GAMEMODE.NumTribes + 1
		GAMEMODE.Tribes[name] = {
		id = GAMEMODE.NumTribes,
		red = red,
		green = green,
		blue = blue,
		Password = Password
		}
		local rp = RecipientFilter()
		rp:AddAllPlayers()
		umsg.Start("newTribe",rp)
			umsg.String(name)
			umsg.Short(GAMEMODE.NumTribes)
			umsg.Short(red)
			umsg.Short(green)
			umsg.Short(blue)
		umsg.End()
		
		team.SetUp(GAMEMODE.NumTribes,tostring(name),Color(red,green,blue,255))
		player:SetTeam(GAMEMODE.NumTribes)
		player:SendMessage("Successfully Created A Tribe",5,Color(255,255,255,255))
		return ""
	elseif (string.find(text,"/join") == 1) then
		local join = string.Explode("\"",text)
		if join[2] and join[2] != "" then
			args = string.Explode(" ",join[3])
			jname = join[2]
			pw = args[2]
		else
			args = string.Explode(" ",text)
			jname = args[2]
			pw = args[3]
		end
		if !jname  then player:ChatPrint("Syntax is: /jointribe \"tribename\" [password(if needed)]") return "" end
		for i,v in pairs(GAMEMODE.Tribes) do
			if string.lower(i) == string.lower(jname) then
				if v.Password and v.Password != pw then player:PrintMessage(3,"Incorrect Tribal Password") return "" end
				player:SetTeam(v.id)
				player:SendMessage("Successfully Joined The Tribe",5,Color(255,255,255,255))
				return ""
			end
		end
		return ""
	elseif (string.find(text,"/leavetribe") == 1) then
				player:SetTeam(1)
				player:SendMessage("Successfully Left The Tribe",5,Color(255,255,255,255))
		return ""
	end
	
	local args = string.Explode(" ",text)
		if args == nil then args = {} end
	if public then
		return GMS.RunChatCmd( player, unpack(args) ) or text
	else
		if GMS.RunChatCmd( player, unpack(args) ) != "" then
			for k,v in pairs(player.GetAll()) do
				if v and v:IsValid() and v:IsPlayer() and v:Team() == player:Team() then
					v:PrintMessage(3, "<TRIBE>" .. player:Nick() .. ": " .. text)
				end
			end
		end
		return ""
	end
end

function GM.SendTribes(player)
for i,v in pairs(GAMEMODE.Tribes) do
	umsg.Start("recvTribes",player)
	umsg.Short(v.id)
	umsg.String(i)
	umsg.Short(v.red)
	umsg.Short(v.green)
	umsg.Short(v.blue)
	umsg.End()
	end
end
hook.Add("PlayerInitialSpawn","getTribes",GM.SendTribes)

function CreateTribe( player, name, red, green, blue, password )
	
	local Password = false
	
	if password and password != "" then
		Password = password
	end
		
	GAMEMODE.NumTribes = GAMEMODE.NumTribes + 1
	GAMEMODE.Tribes[name] = {
	id = GAMEMODE.NumTribes,
	red = red,
	green = green,
	blue = blue,
	Password = Password
	}
	local rp = RecipientFilter()
	rp:AddAllPlayers()
	umsg.Start("newTribe",rp)
		umsg.String(name)
		umsg.Short(GAMEMODE.NumTribes)
		umsg.Short(red)
		umsg.Short(green)
		umsg.Short(blue)
	umsg.End()
	
	team.SetUp(GAMEMODE.NumTribes,tostring(name),Color(red,green,blue,255))
	player:SetTeam(GAMEMODE.NumTribes)
	player:SendMessage("Successfully Created A Tribe",5,Color(255,255,255,255))
end

function CreateTribeCmd( player, cmd, args, argv )
	if !args[4] or args[4] == "" then
		player:ChatPrint("Syntax is: gms_createtribe \"tribename\" red green blue [password(optional)]") return 
	end
	if args[5] and args[5] != "" then
		CreateTribe( player, args[1], args[2], args[3], args[4], args[5] )
	else
		CreateTribe( player, args[1], args[2], args[3], args[4], "" )
	end
end
concommand.Add( "gms_createtribe", CreateTribeCmd )

function joinTribe( player, cmd, args )
	local pw = ""
	if !args[1] or args[1] == "" then
		player:ChatPrint("Syntax is: gms_join \"tribename\" [password(if needed)]") return 
	end
	if args[2] and args[2] != "" then
		pw = args[2]
	end
	for i,v in pairs(GAMEMODE.Tribes) do
		if string.lower(i) == string.lower(args[1]) then
			if v.Password and v.Password != pw then player:PrintMessage(3,"Incorrect Tribal Password") return end
			player:SetTeam(v.id)
			player:SendMessage("Successfully Joined The Tribe",5,Color(255,255,255,255))
		end
	end
end
concommand.Add( "gms_join", joinTribe )

function leaveTribe( player, cmd, args )
	player:SetTeam(1)
	player:SendMessage("Successfully Left The Tribe",5,Color(255,255,255,255))
end
concommand.Add( "gms_leave", leaveTribe )

-- A function to merge resources between boxes by touching.
function GM:MergeResources(entity_A, entity_B) 
	local entity_A_owner = entity_A.owner;
	local entity_B_owner = entity_B.owner;

	if (entity_A_owner != nil and entity_B_owner != nil) then
		if (entity_A_owner == entity_B_owner) then
			local entity = ents.Create("gms_resourcedrop");
			entity:SetPos(entity_A:GetPos());
			entity:SetAngles(entity_A:GetAngles());
			entity:Spawn();
			entity:GetPhysicsObject():Wake();

			entity.Type = entity_A.Type;
			entity.Amount = entity_A.Amount +entity_B.Amount;

			entity:ApplyDropInfo();
			
			entity_A:Remove();
			entity_B:Remove();
			
			entity.owner = ent_a_owner;
		end;
	else
		local entity = ents.Create("gms_resourcedrop");
		entity:SetPos(entity_A:GetPos());
		entity:SetAngles(entity_A:GetAngles());
		entity:Spawn();
		entity:GetPhysicsObject():Wake();

		entity.Type = entity_A.Type;
		entity.Amount = entity_A.Amount +entity_B.Amount;

		entity:ApplyDropInfo();
	
		entity_A:Remove();
		entity_B:Remove();
	end;
end;

-- A function to add resources to a buildsite by touching.
function GM:ApplyBuildsiteResources(resourceDrop, buildsite)
	local resourceOwner = resourceDrop.owner;
	local buildsiteOwner = buildsite.owner;

	if (resourceOwner != nil and buildsiteOwner != nil and resourceDrop:IsPlayerHolding()) then
		if (resourceDrop.Amount > buildsite.Costs[resourceDrop.Type]) then	
			resourceDrop.Amount = resourceDrop.Amount -buildsite.Costs[resourceDrop.Type];
			resourceDrop:ApplyDropInfo();
			
			buildsite.Costs[resourceDrop.Type] = nil;
		elseif (resourceDrop.Amount <= buildsite.Costs[resourceDrop.Type]) then
			buildsite.Costs[resourceDrop.Type] = buildsite.Costs[resourceDrop.Type] -resourceDrop.Amount;
		
			resourceDrop:Remove();
		end;
	
		for k, v in pairs(buildsite.Costs) do
			if (buildsite.Costs[resourceDrop.Type] and buildsite.Costs[resourceDrop.Type] <= 0) then
				buildsite.Costs[resourceDrop.Type] = nil;
			end;
		end;
		
		if (table.Count(buildsite.Costs) > 0) then
			local text = "You need: ";

			for k, v in pairs(buildsite.Costs) do
				text = text .. k .. " (x" .. v .. ")";
			end

			text = text .." to finish.";
			resourceOwner:SendMessage(text, 5, Color(255, 255, 255));
		else
			resourceOwner:SendMessage("Finished!", 3, Color(10, 200, 10));
			buildsite:Finish();         
		end;
	
		local text = ":";
	
		for k, v in pairs(buildsite.Costs) do
			text = text .. " " .. k .. " (x" .. v .. ")";
		end

		buildsite:SetNetworkedString("Resources", text);
	end;
end;

/*---------------------------------------------------------
   Resource Box versus Player Damage
---------------------------------------------------------*/
function playershouldtakedamage(victim, attacker)

	if (victim:IsPlayer() and attacker:GetClass() == "gms_resourcedrop") or (victim:IsPlayer() and attacker:IsPlayerHolding()) then
		return false
	end

	return true
	
end
hook.Add( "PlayerShouldTakeDamage", "playershouldtakedamage", playershouldtakedamage)

/*---------------------------------------------------------
   All Smelt Function
---------------------------------------------------------*/
function PlayerMeta:AllSmelt(ResourceTable)
	local AmountReq = 0;
	local resourcedata = {
		Req = {},
		Results = {}
	};

	for k,v in pairs(ResourceTable.Req) do
		if self:GetResource(k) > 0 then
			if self:GetResource(k) <= ResourceTable.Max then
				resourcedata.Req[k] = self:GetResource(k)
				AmountReq = AmountReq + self:GetResource(k)				
			else
				resourcedata.Req[k] = ResourceTable.Max
				AmountReq = AmountReq + ResourceTable.Max
				self:SendMessage("You can only do " .. tostring(ResourceTable.Max) .. " " .. k .. " at a time.",3,Color(200, 0, 0))
			end
		else
			resourcedata.Req[k] = 1
		end
	end
	for k,v in pairs(ResourceTable.Results) do
		resourcedata.Results[k] = AmountReq
	end
	return resourcedata
end